﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;

using alib.BitArray;
using alib.Collections;
using alib.Collections.ReadOnly;
using alib.Concurrency;
using alib.Debugging;
using alib.Enumerable;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// Type in the type hierarchy. This hierarchy is a fully-connected directed-acyclic graph (DAG) where each type
	/// supports zero or more features that may be arbitrarily constrained to be any type in the hierarchy.
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public sealed partial class Type : LatticeType
	{
		public Type(Grammar g, String name)
			: base(g, name)
		{
		}
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public FeatureConfig fc;
		//public Type t_fc_next;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public sealed override Type InstanceType { get { return this; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Edge.Flag EdgeFlag
		{
			[DebuggerStepThrough]
			get
			{
				if (this == td.ΔString)
					return Edge.Flag.EtmString;
				Edge.Flag f = (Edge.Flag)_id;
				if ((m_flags & Type.Flags.HasAppropriateFeatures) != 0)
					f |= Edge.Flag.EtmNonBareType;
				if (c_children == 0)
					f |= Edge.Flag.EtmLeafType;
				return f;
			}
		}

		public bool HasFeature(int i_feat) { return i_feat >= 0 && fc.ba[i_feat]; }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public void set_top(IRtParent parent)
		{
			Tfs top_tfs = new EmptyFeatureStructureTfs(parent, this);
			//top_tfs.Name = "TFS: *top*";
			this._expanded = this._definition = top_tfs;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override Tfs EnsureExpanded()
		{
			if (!this.HasAppropriateFeatures)
				return Definition;

			Tfs exp = _expand_type();

			if (exp == null)
				throw new TfsException("Error expanding type '{0}': could not unify with its constraint or parents.", this.Name);
			Debug.Assert(exp.Type == this);
			//exp.Name = "Expanded: " + m_name;
			return exp;
		}


		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public Tfs _expand_type()
		{
			Debug.Assert(this.HasAppropriateFeatures);
			RefList<Tfs> exp_parents = _get_parents_for_expand();

			Tfs def = this.Definition;
			Debug.Assert(def.Type == this);

			/// this.Definition preloaded above this point because it can turn 
			/// off the 'HasLocalConstraints' flag
			if ((m_flags & Flags.HasLocalConstraints) == 0)
			{
				if (exp_parents.Count == 0)
					return def;
				if (exp_parents.Count == 1)
				{
					Type sp = exp_parents[0].Type;
					Debug.Assert(sp.fc != ftm.Empty && sp.fc != ftm.EmptyWithFeaturesBelow);
					if (sp.fc != this.fc)
					{
						/* cannot structure share due to root coverage requirement */
					}
					else
					{
						Interlocked.Increment(ref tu.c_structures_shared);
						ArrayTfs ret = new ArrayTfs(bcpo, (ArrayTfs)exp_parents[0]);
						Debug.Assert(ret.Type != this);
						ret._top_edge = tu.CreateEdge(this, ret._top_edge.Mark, false);
						return ret;
					}
				}
			}
			return Unification.UnifyForceExpand(ftm.r_none, this, exp_parents.GetTrimmed());
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RefList<Tfs> _get_parents_for_expand()
		{
			var rl = new RefList<Tfs>(parents.Count);
			foreach (Type p in parents)
			{
				ArrayTfs pexp;
				if (p.HasAppropriateFeatures && (pexp = p.Expanded as ArrayTfs) != null)
				{
					arr_tfs_entry[] rgent = pexp.entries;
					if (!rl.Any(already_have => rgent == ((ArrayTfs)already_have).entries))
						rl.Add(pexp);
				}
			}
			return rl;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public override String ToString()
		{
#if DEBUG && false
			String s = String.Format("{0,4} {1,4}  {2,-30}",
				_id,
				IsGlb ? "glb" : IsTop ? "top" : bit_num.ToString(),
				Name + (c_children == 0 ? " ⊣" : " …"));

			var _tmp = _expanded;
			s += _tmp == null ? "null" : _tmp is BusyTfs ? "busy" : "exp✔";
			s += "  [";
			if (fc != null)
				s += String.Format("fc#{0,-3} c:{1,-2} ", fc.fc_ix, fc.rg_fix.Length);

			s += ((m_flags & (Flags.HasAppropriateFeatures | Flags.HasLocalConstraints)) == Flags.HasAppropriateFeatures ? "Ⓕ" : "◯")
				+ " » "
				+ ((m_flags & Flags.HasLocalConstraints) != 0 ? "Ⓕ" : "◯")
				+ " » "
				+ ((m_flags & Flags.FeaturesBelow) != 0 ? "Ⓕ" : "◯")
				+ "]";
			return s;
#else
			return Name;// +" " + _id.ToString();
#endif
		}
	};
}
